#include "editorview.h"
#include <boost/bind.hpp>
#include <cmath>
#include "font.h"
#include "app.h"
#include "rowlabel.h"
#include "columnlabel.h"
#include "event.h"
#include "scrollskin.h"
using namespace boost;

EditorView::EditorView(App *pApp, Rect rect, Font *pFont)
: View(rect, pFont), m_pEditor(pApp->patternEditor()),
  m_hScroll(pApp->skin()->scrollSkin(), ScrollBar::Horizontal), 
  m_vScroll(pApp->skin()->scrollSkin(), ScrollBar::Vertical), 
  m_pRowLabel(NULL), m_pColLabel(NULL), m_pApp(pApp),
  m_rowOffset(0), m_colOffset(0), m_playerRow(EditorView::NoRow),
  m_playing(false) {

     ScrollSkin *pSkin = m_pApp->skin()->scrollSkin();
     m_pRowLabel = new RowLabel(0, 2, m_pFont, Point(0,0), 0, 0, m_pApp->skin());
     int colCount = m_pEditor->song()->trackCount();
     int rlWidth = m_pRowLabel->rect().width();
     int w = m_rect.width() - pSkin->thickness() - rlWidth;
     m_pColLabel = new ColumnLabel(colCount, m_pFont, 
				   Point(m_rect.x() + rlWidth, m_rect.y()), w);
     int clHeight = m_pColLabel->rect().height();
     m_pRowLabel->setStart(Point(m_rect.x(), m_rect.y() + clHeight));
     m_pRowLabel->setHeight(m_rect.height() - pSkin->thickness() - clHeight);

     // setup scroll bars
     m_hScroll.setMaximum(colCount);
     m_hScroll.setMinimum(wholeVisibleCols());
     setHScrollPos(m_hScroll.minimum());
     // add scrollbars to list of child views so they receive input events
     m_views.push_back(&m_hScroll);
     m_views.push_back(&m_vScroll);
     // connect to scroll bar onPosChange signal
     connect();

     updateRects();
}

void EditorView::connect() {
     // connect to scroll bars onPosChange signal
     m_hScrollConn = m_hScroll.onPosChange(bind(&EditorView::updateColOffset, this, _1));
     m_vScrollConn = m_vScroll.onPosChange(bind(&EditorView::updateRowOffset, this, _1));
}

void EditorView::updateRects() {
     ScrollSkin *pSkin = m_pApp->skin()->scrollSkin();
     // update rowCol
     int rlWidth = m_pRowLabel->rect().width();
     int w = m_rect.width() - pSkin->thickness() - rlWidth;
     m_pColLabel->setStart(Point(m_rect.x() + rlWidth, m_rect.y()));
     m_pColLabel->setWidth(w);
     // update rowLabel
     int clHeight = m_pColLabel->rect().height();
     int h = m_rect.height()  - pSkin->thickness() - clHeight;
     m_pRowLabel->setStart(Point(m_rect.x(), m_rect.y() + clHeight));
     m_pRowLabel->setHeight(h);
     Rect rlRect = m_pRowLabel->rect();
     Rect clRect = m_pColLabel->rect();
     int width = m_rect.width() - pSkin->thickness() - rlRect.width();
     int height = m_rect.height() - pSkin->thickness() - clRect.height();
     // update viewRect
     m_viewRect.setX(rlRect.x()+rlRect.width());
     m_viewRect.setY(m_rect.y()+clRect.height());
     m_viewRect.setWidth(width);
     m_viewRect.setHeight(height);
     // update scroll bars
     int thick = pSkin->thickness();
     m_vScroll.setMinimum(wholeVisibleRows());
     setVScrollPos(m_vScroll.minimum() + m_rowOffset);
     m_vScroll.setStart(Point(m_rect.width() - thick, m_rect.y() + clRect.height()));
     m_vScroll.setLength(m_rect.height() - thick - clRect.height());
     m_hScroll.setMinimum(wholeVisibleCols());
     setHScrollPos(m_hScroll.minimum() + m_colOffset);
     m_hScroll.setStart(Point(m_rect.x() + rlRect.width(), m_rect.y() + m_rect.height() - thick));
     m_hScroll.setLength(m_rect.width() - thick - rlRect.width());
}

EditorView::~EditorView() {
     delete m_pRowLabel;
     delete m_pColLabel;
}

int EditorView::visibleRows() {
     return ceil((float)m_viewRect.height() / (float)m_pFont->height());
}

int EditorView::wholeVisibleRows() {
     return floor((float)m_viewRect.height() / (float)m_pFont->height());
}

int EditorView::visibleCols() {
     return ceil((float)m_viewRect.width() / (float)colWidth());
}

int EditorView::wholeVisibleCols() {
     return floor((float)m_viewRect.width() / (float)colWidth());
}

int EditorView::colWidth() {
     return Event::strlen * m_pFont->width() + EditorView::LineThickness + 2;
}

void EditorView::setRowOffset(int rowOffset) {
     m_rowOffset = rowOffset;
     m_pRowLabel->setOffset(m_rowOffset);
     setVScrollPos(rowOffset + m_vScroll.minimum());
}

void EditorView::setColOffset(int colOffset) {
     m_colOffset = colOffset;
     m_pColLabel->setOffset(m_colOffset);
     setHScrollPos(colOffset + m_hScroll.minimum());
}

void EditorView::updateRowOffset(int rowOffset) {
     m_rowOffset = rowOffset - m_vScroll.minimum();
     m_pRowLabel->setOffset(m_rowOffset);
}

void EditorView::updateColOffset(int colOffset) {
     m_colOffset = colOffset - m_hScroll.minimum();
     m_pColLabel->setOffset(m_colOffset);
}

// block scroll bar connection slot while updating
void EditorView::setVScrollPos(int pos) {
     signals2::shared_connection_block block(m_vScrollConn);
     m_vScroll.setPos(pos);
}

// block scroll bar connection slot while updating
void EditorView::setHScrollPos(int pos) {
     signals2::shared_connection_block block(m_hScrollConn);
     m_hScroll.setPos(pos);
}
